#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import gstage4
import strict_portage
import multiprocessing
from fm_util import Util
from fm_util import CloudCacheGentoo
from fm_util import CcacheLocalService
from fm_util import PrintLoadAvgThread
from fm_param import FmConst


class Stage4GentooBuilder:

    def __init__(self, infoPrinter, cloudOverlayDb, machine):
        self._stage4Info = {
            "amd64": {
                "subarch": "amd64",
                "variant": "systemd",
                "profile": "default/linux/amd64/23.0",
                "stage3-file": None,
            },
            "arm64": {
                "subarch": "arm64",
                "variant": "systemd",
                "profile": None,
                "stage3-file": None,
            },
        }
        self._p = infoPrinter
        self._cloudOverlayDb = cloudOverlayDb
        self._machine = machine
        self._snapshotFile = None

    def downloadFiles(self, bTitle):
        if bTitle:
            self._p.printInfo("- Downloading %s files..." % (Util.getOsName("gentoo")))

        cache = CloudCacheGentoo(FmConst.gentooLinuxCacheDir)
        cache.sync()
        for arch, v in self._stage4Info.items():
            assert arch in cache.get_arch_list()
            assert v["subarch"] in cache.get_subarch_list(arch)
        for arch, v in self._stage4Info.items():
            try:
                v["stage3-file"] = cache.get_latest_stage3(arch, v["subarch"], v["variant"], cached_only=True)   # prefer local stage3 file
            except FileNotFoundError:
                v["stage3-file"] = cache.get_latest_stage3(arch, v["subarch"], v["variant"])
        self._snapshotFile = cache.get_latest_snapshot()                                                         # always use newest snapshot

    def build(self, arch, workDir):
        ftFixbug = gstage4.target_features.FixBugs()
        ftKernel = gstage4.target_features.UseBbki()
        ftPackageManager = gstage4.target_features.UsePortage()
        ftServiceManager = gstage4.target_features.UseSystemd()
        ftNoDeprecate = gstage4.target_features.DontUseDeprecatedPackagesAndFunctions()
        ftPreferGnu = gstage4.target_features.PreferGnuAndGpl()
        ftSupportAllTermType = gstage4.target_features.SupportAllTermType()
        ftTailorGit = gstage4.target_features.TailorGit(add_items=[
            "http-connection-timeout-extension",
            "robust-extension",
        ])
        ftTailorWget = gstage4.target_features.TailorWget(add_items=[
            "robust-extension",
        ])
        ftTailorRsync = gstage4.target_features.TailorRsync(add_items=[
            "robust-extension",
        ])
        ftFpemudOs = TargetFeatureFpemudOs()

        # step
        self._p.printInfo("- Initializing...")
        c = CcacheLocalService()
        wdir = gstage4.WorkDir(workDir, reset=True)
        builder = None
        if True:
            s = gstage4.Settings()
            s.program_name = FmConst.programName
            s.log_dir = None
            s.verbose_level = 0
            s.host_cpu_core_count = multiprocessing.cpu_count()
            s.host_memory_size = Util.getPhysicalMemorySize() * 1024 * 1024 * 1024
            s.host_cooling_level = 10 if Util.hwcfgHasFan(self._machine) is not None else 1
            s.host_distfiles_dir = FmConst.distDir
            if c.is_enabled():
                s.host_ccache_dir = c.get_ccache_dir()

            ts = gstage4.TargetSettings()
            ts.arch = arch
            ts.profile = self._stage4Info[arch]["profile"]
            ts.mirrors = strict_portage.cfg.PortageConfigDir().get_mirrors_obj().get_mirror_mapping()
            ftFixbug.update_target_settings(ts)
            ftKernel.update_target_settings(ts)
            ftPackageManager.update_target_settings(ts)
            ftServiceManager.update_target_settings(ts)
            ftNoDeprecate.update_target_settings(ts)
            ftPreferGnu.update_target_settings(ts)
            ftTailorGit.update_target_settings(ts)
            ftTailorWget.update_target_settings(ts)
            ftTailorRsync.update_target_settings(ts)
            ftFpemudOs.update_target_settings(ts)
            if c.is_enabled():
                # we use ccache if host uses ccache
                ts.build_opts.ccache = True

            builder = gstage4.Builder(s, ts, wdir)

        # step
        self._p.printInfo("- Extracting seed stage...")
        with gstage4.seed_stages.GentooStage3Archive(*self._stage4Info[arch]["stage3-file"]) as ss:
            builder.action_unpack(ss)

        # step
        self._p.printInfo("- Installing gentoo repository...")
        gentooRepo = gstage4.repositories.GentooSnapshot(self._snapshotFile)
        builder.action_create_gentoo_repository(gentooRepo)

        # step
        self._p.printInfo("- Generating configurations...")
        if True:
            builder.action_init_confdir()

        # step
        self._p.printInfo("- Installing overlays...")
        if True:
            builder.action_create_overlays([
                gstage4.repositories.UserDefinedOverlay("guru", *self._cloudOverlayDb.getOverlayVcsTypeAndUrl("guru")),
                gstage4.repositories.UserDefinedOverlay("mirrorshq-overlay", "git", "https://gitee.com/mirrorshq/gentoo-overlay"),
                gstage4.repositories.UserDefinedOverlay("your-own-os-overlay", "git", "https://gitee.com/your-own-os/gentoo-overlay"),
                gstage4.repositories.UserDefinedOverlay("fpemud-overlay", "git", "https://gitee.com/fpemud/gentoo-overlay"),
            ])

        # step
        with PrintLoadAvgThread(self._p.getIndentStr() + "- Updating world..."):
            worldSet = {
                "dev-vcs/git",
                "dev-vcs/subversion",
                "net-misc/wget",
            }
            if c.is_enabled():
                worldSet.add("dev-util/ccache")
            ftPackageManager.update_world_set(worldSet)
            ftServiceManager.update_world_set(worldSet)
            ftSupportAllTermType.update_world_set(worldSet)

            builder.action_update_world(worldSet)

        # step: this step does nothing
        builder.action_install_kernel()

        # step: this step does nothing
        builder.action_enable_services(service_list=[])

        # step
        self._p.printInfo("- Initializing Fpemud OS...")
        s.verbose_level = 2                                                     # FIXME
        if True:
            # FIXME, should be integrated into "sysman check" or "sysman init"
            ftPamSuWheelOk = gstage4.target_features.PamSuWheelOk()
            builder.add_and_run_custom_action("pam_su_wheel_ok", ftPamSuWheelOk.get_custom_action())

            builder.add_and_run_custom_action("init_fpemud_os", FpemudOsCustomActions.init_fpemud_os())

            builder.add_and_run_custom_action("robustify_by_barkspider", FpemudOsCustomActions.robustify_by_barkspider())

        # step
        self._p.printInfo("- Installing extra packages...")
        builder.add_and_run_custom_action("install_extra_packages", FpemudOsCustomActions.install_extra_packages())

        # step
        self._p.printInfo("- Cleaning up...")
        builder.add_and_run_custom_action("revert_etc_dir", FpemudOsCustomActions.revert_etc_dir())
        builder.action_cleanup()

        # finish
        builder.finish()

        return wdir


class FpemudOs:

    def __init__(self):
        self.ftFixbug = gstage4.target_features.FixBugs()
        self.ftKernel = gstage4.target_features.UseBbki()
        self.ftPackageManager = gstage4.target_features.UsePortage()
        self.ftServiceManager = gstage4.target_features.UseSystemd()
        self.ftAcceptAllLicenses = gstage4.target_features.AcceptAllLicenses()
        self.ftNoDeprecate = gstage4.target_features.DontUseDeprecatedPackagesAndFunctions()
        self.ftDeNeutral = gstage4.target_features.DesktopEnvironmentNeutral()
        self.ftUseCapability = gstage4.target_features.UseCapability()
        self.ftUseGnomeKeyring = gstage4.target_features.UseGnomeKeyring()
        self.ftUseFcitx = gstage4.target_features.UseFcitx()
        self.ftPreferGnu = gstage4.target_features.PreferGnuAndGpl()
        self.ftPreferSystemComponent = gstage4.target_features.PreferSystemComponent()
        self.ftPreferWayland = gstage4.target_features.PreferWayland()
        self.ftPreferPipewire = gstage4.target_features.PreferPipewire()
        self.ftPreferBlockDeviceUAccess = gstage4.target_features.PreferBlockDeviceUAccess()
        self.ftPreferLibtorrentRasterbar = gstage4.target_features.PreferLibtorrentRasterbar()
        self.ftPreferPythonMagic = gstage4.target_features.PreferPythonMagic()
        self.ftNoVt = gstage4.target_features.DontUseVT()
        self.ftDisablePcSpeaker = gstage4.target_features.DisablePcSpeaker()            # FIXME
        self.ftDisableFstab = gstage4.target_features.DisableFstab()
        self.ftNotUsePypy = gstage4.target_features.DontUsePypy()
        self.ftSupportAll = [
            gstage4.target_features.SupportAllVideoFormat(),
            gstage4.target_features.SupportAllAudioFormat(),
            gstage4.target_features.SupportAllImageFormat(),
            gstage4.target_features.SupportAllDocumentFormat(),
            gstage4.target_features.SupportAllCompressFormat(),
            gstage4.target_features.SupportAllGraphicsApi(prefer_wayland=True),
        ]
        self.ftSupportAllTermType = gstage4.target_features.SupportAllTermType()
        self.ftMemTest = gstage4.target_features.MemTest()
        self.ftChrony = gstage4.target_features.Chrony(exclusive=True)
        if "net-wireless/iwd" in strict_portage.cfg.World().get_package_names():
            self.ftNetworkManager = gstage4.target_features.NetworkManager(wifi=True, exclusive=True)
        else:
            self.ftNetworkManager = gstage4.target_features.NetworkManager(wifi=False, exclusive=True)
        if "net-dns/avahi" in strict_portage.cfg.World().get_package_names():
            self.ftZeroConf = gstage4.target_features.UseZeroConf(service="avahi")
        else:
            self.ftZeroConf = gstage4.target_features.DontUseZeroConf()
        if "net-wireless/bluez" in strict_portage.cfg.World().get_package_names():
            self.ftBluetooth = gstage4.target_features.UseBluetooth(service="bluez")
        else:
            self.ftBluetooth = gstage4.target_features.DontUseBluetooth()
        if "net-print/cups-meta" in strict_portage.cfg.World().get_package_names():
            self.ftPrinting = gstage4.target_features.UsePrinting(service="cups")
        else:
            self.ftPrinting = gstage4.target_features.DontUsePrinting()
        self.ftUseAllLlvmTargets = gstage4.target_features.UseAllLlvmTargets()
        self.ftUseAllQemuTargets = gstage4.target_features.UseAllQemuTargets()
        self.ftNoPolkit = gstage4.target_features.DontUsePolicyKit()
        self.ftNoLogrotate = gstage4.target_features.DontUseLogrotate()
        self.ftNoSudo = gstage4.target_features.DontUseSudo()
        self.ftTailorBaselayout = gstage4.target_features.TailorBaselayout(remove_items=[
            "/etc/shells",
        ])
        self.ftTailorUtilLinux = gstage4.target_features.TailorUtilLinux(remove_items=[
            "runuser",
        ])
        self.ftTailorSystemd = gstage4.target_features.TailorSystemd(
            disable_items=[
                "systemd-udevd-socket-activation",
                "kmod-static-nodes",
            ],
            remove_items=[
                "systemd-battery-check",
                "systemd-boot",
                "systemd-coredump",                         # diagnosing things are beyound us
                "systemd-dissect",
                "systemd-hostnamed",                        # we don't change hostname dynamically
                "systemd-firstboot",
                "systemd-kexec",
                "systemd-localed",                          # "eselect locale" is enough
                "systemd-machined",
                "systemd-networkd",
                "systemd-oomd",
                "systemd-portabled",
                "systemd-pstore",
                "systemd-resolvd",
                "systemd-storagetm",
                "systemd-sysext",
                "systemd-sysupdate",
                "systemd-sysusers",
                "systemd-timedated",                        # "eselect timezone" is enough
                "systemd-timesyncd",                        # chrony is better
                "systemd-update",
                "systemd-userdbd",
                "systemd-ssh-proxy",
                "systemd-run-generator",                    # we don't tell systemd to run something through kernel command line
                "systemd-efi-boot-generator",               # we mount /boot in initramfs so this is uneccessary
                "systemd-gpt-auto-generator",               # all the key mount point are established in initramfs, no automation needed
                "systemd-ssh-generator",
                "kernel-management",
                "fstab",
                "fs-operations",
                "ldconfig.service",                         # we update ld cache during package install, not system boot
                "initrd-facility",                          # we don't use systemd in initramfs
                "debug-facility",
            ]
        )
        self.ftTailorShadow = gstage4.target_features.TailorShadow(remove_items=[
            "logoutd",                                  # we don't use /etc/porttime
            "chfn",                                     # we don't use user finger info, it's too old and too limited
            "chsh",                                     # FIXME: we use a simpler implementation
            "expiry",                                   # we don't use password expiry
            "groupmems",                                # we use strict_pgs, no extra member is allowed for a user's primary group
            "old-group-operations",
        ])
        self.ftTailorPam = gstage4.target_features.TailorPam(remove_items=[
            "pam_access",           # it uses /etc/security
            "pam_env_conf",         # it uses /etc/security, remove /etc/security/pam_env.conf only
            "pam_faillock_conf",    # it uses /etc/security, remove /etc/security/faillock.conf only
            "pam_group",            # it uses /etc/security
            "pam_limits",           # it uses /etc/security
            "pam_namespace",        # it uses /etc/security
            "pam_pwhistory",        # it uses /etc/security
            "pam_time",             # it uses /etc/security, the whole /etc/security directory would be removed with these items specified
        ])
        self.ftTailorAvahi = gstage4.target_features.TailorAvahi(disable_items=[
            "auto-activation",
        ])
        self.ftTailorEselect = gstage4.target_features.TailorEselect(remove_items=[
            "kernel-module",
            "rc-module",
        ])
        self.ftTailorGit = gstage4.target_features.TailorGit(add_items=[
            "http-connection-timeout-extension",
            "robust-extension",
        ])
        self.ftTailorWget = gstage4.target_features.TailorWget(add_items=[
            "robust-extension",
        ])
        self.ftTailorRsync = gstage4.target_features.TailorRsync(add_items=[
            "robust-extension",
        ])
        # self.ftTailorQemu = gstage4.target_features.TailorQemu(add_items=[
        #     "3dfx-patch",
        # ])
        self.ftTailorWine = gstage4.target_features.TailorWine(disable_items=[
            "auto-adding-menu-entries",
        ])
        self.ftUseGithubMirror = gstage4.target_features.UseGithubMirror()
        self.ftUseGogMirror = gstage4.target_features.UseGogMirror()
        self.ftUseHbMirror = gstage4.target_features.UseHbMirror()
        self.ftUseHuggingFaceMirror = gstage4.target_features.UseHuggingFaceMirror()
        self.ftSystemService = gstage4.target_features.AddSystemServiceUseFlag()
        self.ftFpemudOs = TargetFeatureFpemudOs()

    def update_target_settings(self, ts):
        self.ftFixbug.update_target_settings(ts)
        self.ftKernel.update_target_settings(ts)
        self.ftPackageManager.update_target_settings(ts)
        self.ftServiceManager.update_target_settings(ts)
        self.ftAcceptAllLicenses.update_target_settings(ts)
        self.ftNoDeprecate.update_target_settings(ts)
        self.ftDeNeutral.update_target_settings(ts)
        self.ftUseCapability.update_target_settings(ts)
        self.ftUseGnomeKeyring.update_target_settings(ts)
        self.ftUseFcitx.update_target_settings(ts, for_wayland=True, for_x11=False)
        self.ftPreferGnu.update_target_settings(ts)
        self.ftPreferSystemComponent.update_target_settings(ts)
        self.ftPreferWayland.update_target_settings(ts)
        self.ftPreferPipewire.update_target_settings(ts)
        self.ftPreferBlockDeviceUAccess.update_target_settings(ts)
        self.ftPreferLibtorrentRasterbar.update_target_settings(ts)
        self.ftPreferPythonMagic.update_target_settings(ts)
        self.ftNoVt.update_target_settings(ts)
        self.ftDisableFstab.update_target_settings(ts)
        self.ftNotUsePypy.update_target_settings(ts)
        for x in self.ftSupportAll:
            x.update_target_settings(ts)
        self.ftMemTest.update_target_settings(ts)
        self.ftChrony.update_target_settings(ts)
        self.ftNetworkManager.update_target_settings(ts)
        if hasattr(self.ftZeroConf, "update_target_settings"):
            self.ftZeroConf.update_target_settings(ts)
        if hasattr(self.ftBluetooth, "update_target_settings"):
            self.ftBluetooth.update_target_settings(ts)
        if hasattr(self.ftPrinting, "update_target_settings"):
            self.ftPrinting.update_target_settings(ts)
        self.ftUseAllLlvmTargets.update_target_settings(ts)
        self.ftUseAllQemuTargets.update_target_settings(ts)
        self.ftNoPolkit.update_target_settings(ts)
        self.ftNoLogrotate.update_target_settings(ts)
        self.ftNoSudo.update_target_settings(ts)
        self.ftTailorBaselayout.update_target_settings(ts)
        self.ftTailorUtilLinux.update_target_settings(ts)
        self.ftTailorSystemd.update_target_settings(ts)
        self.ftTailorShadow.update_target_settings(ts)
        self.ftTailorPam.update_target_settings(ts)
        self.ftTailorAvahi.update_target_settings(ts)
        self.ftTailorEselect.update_target_settings(ts)
        self.ftTailorGit.update_target_settings(ts)
        self.ftTailorWget.update_target_settings(ts)
        self.ftTailorRsync.update_target_settings(ts)
        # self.ftTailorQemu.update_target_settings(ts)
        self.ftTailorWine.update_target_settings(ts)
        self.ftUseGithubMirror.update_target_settings(ts)
        self.ftUseGogMirror.update_target_settings(ts)
        self.ftUseHbMirror.update_target_settings(ts)
        self.ftUseHuggingFaceMirror.update_target_settings(ts)
        self.ftSystemService.update_target_settings(ts)
        self.ftFpemudOs.update_target_settings(ts)
        self.ftFpemudOs.update_target_settings_for_sessions(ts)

    def update_world_set(self, worldSet):
        self.ftPackageManager.update_world_set(worldSet)
        self.ftServiceManager.update_world_set(worldSet)
        self.ftSupportAllTermType.update_world_set(worldSet)
        self.ftChrony.update_world_set(worldSet)
        self.ftNetworkManager.update_world_set(worldSet)
        if hasattr(self.ftZeroConf, "update_world_set"):
            self.ftZeroConf.update_world_set(worldSet)
        if hasattr(self.ftBluetooth, "update_world_set"):
            self.ftBluetooth.update_world_set(worldSet)
        if hasattr(self.ftPrinting, "update_world_set"):
            self.ftPrinting.update_world_set(worldSet)

    def update_service_list(self, serviceList):
        self.ftChrony.update_service_list(serviceList)
        self.ftNetworkManager.update_service_list(serviceList)
        if hasattr(self.ftZeroConf, "update_service_list"):
            self.ftZeroConf.update_service_list(serviceList)
        if hasattr(self.ftBluetooth, "update_service_list"):
            self.ftBluetooth.update_service_list(serviceList)
        if hasattr(self.ftPrinting, "update_service_list"):
            self.ftPrinting.update_service_list(serviceList)

    def checkPortageConfigDirByTargetSettings(self, dirObj, c, ts, bAutoFix, errCb):
        # check /etc/portage/repo.postsync.d directory
        if isinstance(dirObj, strict_portage.cfg.RepoPostSyncDir):
            assert len(ts.repo_postsync_patch_directories) > 0
            scriptFn = "10-" + os.path.basename(gstage4.REPO_POSTSYNC_PATCH_SCRIPT)
            scriptDirFn = scriptFn + ".d"
            for fn in ts.repo_postsync_patch_directories:
                fullfn = os.path.join(dirObj.path, scriptDirFn, fn)
                if not os.path.islink(fullfn) or os.readlink(fullfn) != os.path.join(gstage4.REPO_POSTSYNC_PATCH_SOURCE_DIR, fn):
                    if bAutoFix:
                        Util.forceSymlink(os.path.join(gstage4.REPO_POSTSYNC_PATCH_SOURCE_DIR, fn), fullfn)
                    else:
                        errCb("invalid symlink \"%s\"" % (fullfn))
            return

        # check /etc/portage/package.license file
        if isinstance(dirObj, strict_portage.cfg.PackageLicense):
            c.check_file(content="".join(["%s %s\n" % (k, v) for k, v in ts.pkg_license.items()]))
            assert len(ts.pkg_license_files) == 0
            return

        # check /etc/portage/package.mask directory
        if isinstance(dirObj, strict_portage.cfg.PackageMask):
            for fn, buf in ts.pkg_mask_files.items():   # gstage4 files
                c.check_member_file(fn, buf)            # FIXME: need to process self.ts.pkg_mask
            return

        # check /etc/portage/package.use directory
        if isinstance(dirObj, strict_portage.cfg.PackageUse):
            for fn, buf in ts.pkg_use_files.items():    # gstage4 files
                c.check_member_file(fn, buf)            # FIXME: need to process self.ts.pkg_use
            return

        # check /etc/portage/package.env and /etc/portage/env directory
        if isinstance(dirObj, strict_portage.cfg.PackageEnv):
            data = ts.install_mask_files.copy()
            assert "00-common" not in data and all([len(x) >= 1 for x in data.values()])
            if len(ts.install_mask) > 0:
                data["00-common"] = {
                    "*/*": ts.install_mask
                }

            for file_name, obj in data.items():
                envData = dict()
                buf = ""
                i = 1
                for pkgWildcard, installMaskList in obj.items():
                    if len(obj) > 1:
                        innerFn = "%s-%d.env" % (file_name, i)
                    else:
                        innerFn = "%s.env" % (file_name)
                    envData[innerFn] = "".join(['INSTALL_MASK="${INSTALL_MASK} %s"\n' % (x) for x in installMaskList])
                    buf += "%s %s\n" % (pkgWildcard, innerFn)
                    i += 1
                c.check_member_file(file_name, content=buf, env_data=envData)

            return

        assert False


class TargetFeatureFpemudOs:

    @staticmethod
    def getSupportedSessionTypes():
        # backend (old name: access):
        #   seat:       (old name: physical) the session owns the whole seat.
        #   compositor: the seat is controlled by a compositor (it is a virtual concept, it may be composed by multiple
        #               separate components) which fully manages input, graphics, sound, etc.
        #               the session runs on this compositor.
        #   ssh:        the session runs as a ssh server session, using basic SSH protocol underhood, without any extension.
        #   rdp:        the session runs as a rdp server session.
        #   vnc:        the session runs as a vnc server session, using standard VNC protocol underhood, with no extension.
        #   tigervnc:   the session runs as a tigervnc server session.
        #   realvnc:    the session runs as a realvnc server session.
        #   frcli:      the session runs as a frcli server session.
        #               frcli server uses "Fpemud Remote Command Line Interface" protocol, which is a dirty but feature-rich protocol composed of ssh, mosh, pipewire-on-net etc.
        #   fvnc:       the session runs as a fvnc server session.
        #               frcli server uses "Fpemud VNC" protocol, which is a dirty but feature-rich protocol composed of vnc, pipewire-on-net etc.
        #
        # ui:
        #   cli:        provides a command line shell.
        #   acli:       provides "cli ui", and offer features similar to Kitty for displaying images and videos, supports audio I/O, and various peripherals are also functional.
        #   wayfire:    provides a wayfire based desktop environment.
        #   fwayfire:   (old name: gui) provides "wayfire ui", but customized by fpemud.
        #   kodi:       provides a KODI user interface.
        #   fkodi:      provides "kodi ui", but customized by fpemud.
        #   vrwayfire:  provides a wayfire desktop environment, with VR functionality
        #   fvrwayfire: provides "wayfire ui", but customized by fpemud.
        #   vrkodi:     provides a KODI user interface, with VR functionality
        #   fvrkodi:    provides "kodi ui", but customized by fpemud.
        #   osjs:       provides OS.js web user interface, with all advanced function such as peripheral redirection and 3D acceleration.
        #   mswin_7:    users operates a Microsoft Windows 7 virtual machine as if it were this system, with all peripherals being managed by the virtual machine.
        #
        # business:
        #   workstation: As a workstation
        #   ecenter: As an entertainment center
        #   pterm: As a personal terminal (such as mobile-phone)
        #

        return [
            ("physical", "gui", "workstation"),     # should be replaced by ("seat", "fwayfire", "workstation")
            # ("seat", "cli", "workstation"),
            # ("seat", "acli", "workstation"),
            # ("seat", "wayfire", "workstation"),
            # ("seat", "fwayfire", "workstation"),
            # ("seat", "vrwayfire", "workstation"),
            # ("seat", "fvrwayfire", "workstation"),
            # ("seat", "mswin_7", "workstation"),
            # ("seat", "kodi", "ecenter"),
            # ("seat", "fkodi", "ecenter"),
            # ("seat", "vrkodi", "ecenter"),
            # ("seat", "fvrkodi", "ecenter"),
            # ("ssh", "cli", "workstation"),
            # ("frcli", "cli", "workstation"),
            # ("frcli", "acli", "workstation"),
            # ("frcli", "acli", "ecenter"),
            # ("fvnc", "wayfire", "workstation"),
            # ("fvnc", "fwayfire", "workstation"),
            # ("fvnc", "mswin_7", "workstation"),
            # ("fvnc", "kodi", "ecenter"),
            # ("fvnc", "fkodi", "ecenter"),
        ]

    def update_target_settings(self, target_settings):
        assert "80-fpemud-os" not in target_settings.pkg_use_files
        assert "80-fpemud-os" not in target_settings.pkg_mask_files

        target_settings.pkg_use_files["80-fpemud-os"] = self._useFileContent.strip("\n") + "\n"

        buf = ""
        buf += self._maskFileContent.strip("\n") + "\n"
        buf += "\n"
        buf += self._maskDeprecatedFileContent.strip("\n") + "\n"
        target_settings.pkg_mask_files["80-fpemud-os"] = buf

    def update_target_settings_for_sessions(self, target_settings):
        assert "80-fpemud-os-sessions" not in target_settings.pkg_use_files

        buf = "app-admin/fpemud-os-gentoo-scripts"
        for access, ui, business in self.getSupportedSessionTypes():
            buf += " session-%s-%s-%s" % (access, ui, business)
        buf += "\n"
        target_settings.pkg_use_files["80-fpemud-os-sessions"] = buf

    _useFileContent = """
# don't build service files by default (needs gstage4.AddSystemServiceUseFlag)
*/*                                                                                                        -system-service

# no lvm
sys-boot/grub                                                                                              -device-mapper

# don't use gentoo branding
*/*                                                                                                        -branding

# eliminate /etc/local.d and some other Gentoo features
sys-apps/systemd                                                                                           vanilla

# eliminate /usr/bin/poweroff and friends
sys-apps/systemd                                                                                           -sysv-utils

# we start fcitx using usrman-session
app-i18n/fcitx                                                                                             -autostart

# enable game expansion pack
games-strategy/command-and-conquer-redalert                                                                counterstrike aftermath
games-strategy/total-annihilation                                                                          core-contingency battle-tactics
"""

    _maskFileContent = """
# use gvfs to handle user-mount & auto-mount
sys-apps/pmount
sys-apps/uam
sys-apps/udevil

# use the nscd in glibc
sys-apps/unscd

# use qemu directly, so libvirt is not needed
app-emulation/libvirt

# kerberos is too complex to be used
virtual/krb5

# have a too complex concept, break the simplicity of the whole system
www-apache/pwauth

# I can't change setting for app-i18n/ibus-pinyin (bug?), ibus-sunpinyin, ibus-googlepinyin are good alternatives
app-i18n/ibus-pinyin

# these packages installs key files in /usr/src, which is bad
dev-cpp/gmock

# tcp-wrappers is not a full-fledged firewall
net-misc/tcp-wrappers

# /usr/bin/dotlock has SGID bit set, why?
net-mail/mailutils

# LVM would be good if it doesn't use config files in /etc
sys-fs/lvm2

# it is a standalone program, somehow conflicts with net-p2p/bitcoind
#net-p2p/bitcoin-qt

# I think using realtime feature in Linux is not good, and, as a background component, it seems rtkit should not depend on polkit
sys-auth/rtkit
acct-user/rtkit
acct-group/rtkit

# snort is old, and it manipulates /usr/src, use net-analyzer/suricata instead
net-analyzer/snort

# of course we should not use rpm
app-arch/rpm

# we use sys-fs/fusefat from your-own-os overlay
sys-fs/libfat

# we use net-p2p/monero-gui::booboo, that package integrates better with the whole system
net-p2p/monero-gui-bin
"""

    _maskDeprecatedFileContent = """
# app-misc/profiled & app-misc/mplugd replaces them all, in doubt
sys-power/acpid
sys-power/cpufreqd
sys-power/cpufrequtils
sys-power/ncpufreqd
"""


class FpemudOsCustomActions:

    @staticmethod
    def init_fpemud_os():
        custom_script_list = []

        pkgList = [
            "app-admin/fpemud-os-gentoo-scripts",
            "app-admin/fpemud-os-in-focus-db",
        ]
        custom_script_list.append(gstage4.scripts.OneLinerScript("emerge %s" % (" ".join(pkgList))))

        s = gstage4.scripts.PlacingFilesScript()
        if os.path.exists("/etc/machine-info"):
            s.append_host_file("/etc/machine-info")
        if os.path.exists("/etc/machine-hwcfg"):
            s.append_host_file("/etc/machine-hwcfg")
        s.append_host_dir("/etc/mrget")
        custom_script_list.append(s)

        custom_script_list.append(gstage4.scripts.OneLinerScript("FPEMUD_OS_PREPARE=1 sysman init"))        # from here on, sysman controls /etc/portage

        custom_script_list.append(gstage4.scripts.OneLinerScript("FPEMUD_OS_PREPARE=1 sysman update"))

        return gstage4.custom_actions.SimpleCustomAction(*custom_script_list)

    @staticmethod
    def robustify_by_barkspider():
        custom_script_list = []

        custom_script_list.append(gstage4.scripts.OneLinerScript("emerge barkspider"))

        # re-install packages so that they can be robustified
        custom_script_list.append(gstage4.scripts.OneLinerScript("emerge -1 dev-vcs/git net-misc/rsync net-misc/wget"))

        # change configure so that the applications can be robustified
        custom_script_list.append(gstage4.scripts.OneLinerScript("barkspider robustify all"))

        return gstage4.custom_actions.SimpleCustomAction(*custom_script_list)

    @staticmethod
    def install_extra_packages():
        packageSet = set()
        serviceList = []
        custom_script_list = []

        ftSshServer = gstage4.target_features.SshServer()
        ftSshServer.update_world_set(packageSet)
        ftSshServer.update_service_list(serviceList)

        ftChrony = gstage4.target_features.Chrony(exclusive=True)
        ftChrony.update_world_set(packageSet)
        ftChrony.update_service_list(serviceList)

        ftNetworkManager = gstage4.target_features.NetworkManager(wifi=True, exclusive=True)
        ftNetworkManager.update_world_set(packageSet)
        ftNetworkManager.update_service_list(serviceList)

        ftBluetooth = gstage4.target_features.UseBluetooth(service="bluez")
        ftBluetooth.update_world_set(packageSet)
        ftBluetooth.update_service_list(serviceList)

        for access, ui, business in TargetFeatureFpemudOs.getSupportedSessionTypes():
            if access == "physical" and ui == "gui" and business == "workstation":
                packageSet.add("app-editors/vscode")                # some extensions crashes in app-editors/vscodium
                packageSet.add("app-office/libreoffice")
                packageSet.add("app-text/tree")
                packageSet.add("dev-util/meld")
                packageSet.add("media-gfx/gimp")
                packageSet.add("media-fonts/noto")
                packageSet.add("media-fonts/noto-cjk")
                packageSet.add("media-fonts/noto-emoji")
                packageSet.add("www-client/chromium")
                packageSet.add("x11-terms/kitty")

        for p in packageSet:
            custom_script_list.append(gstage4.scripts.OneLinerScript("FPEMUD_OS_PREPARE=1 sysman install-package %s" % (p)))

        # installed package can affect system config reversely
        # for example, net-wireless/bluez in @world can affect USE flags
        custom_script_list.append(gstage4.scripts.OneLinerScript("FPEMUD_OS_PREPARE=1 sysman update --use-cache"))      # FIXME: sysman update should not rebuild kernel here

        for s in serviceList:
            custom_script_list.append(gstage4.scripts.OneLinerScript("systemctl enable %s -q" % (s)))

        return gstage4.custom_actions.SimpleCustomAction(*custom_script_list)

    @staticmethod
    def revert_etc_dir():
        custom_script_list = [
            gstage4.scripts.OneLinerScript("rm -f /etc/machine-info"),
            gstage4.scripts.OneLinerScript("rm -f /etc/machine-hwcfg"),
            gstage4.scripts.OneLinerScript("rm -rf /etc/mrget"),
        ]
        return gstage4.custom_actions.SimpleCustomAction(*custom_script_list)
